package cn.chenfeifx.media.controls.skin;

import cn.chenfeifx.core.common.TimelineBuilder;
import cn.chenfeifx.core.controls.CFImage;
import cn.chenfeifx.media.controls.CFAudioPlayer;
import cn.chenfeifx.media.controls.MediaPlayerSlider;
import cn.chenfeifx.media.controls.common.TimeUtils;
import javafx.animation.Animation;
import javafx.animation.Timeline;
import javafx.beans.binding.Bindings;
import javafx.collections.ObservableList;
import javafx.collections.ObservableMap;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SkinBase;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.transform.Rotate;
import javafx.util.Duration;
import org.kordamp.ikonli.Ikon;
import org.kordamp.ikonli.boxicons.BoxiconsRegular;
import org.kordamp.ikonli.boxicons.BoxiconsSolid;
import org.kordamp.ikonli.javafx.FontIcon;

import java.util.concurrent.Callable;

public class CFAudioPlayerSkin extends SkinBase<CFAudioPlayer> {

    public CFAudioPlayerSkin(CFAudioPlayer control) {
        super(control);
        getChildren().add(new AudioPlayerPane());
    }

    public Media getMedia() {
        return getSkinnable().getMedia();
    }


    public class AudioPlayerPane extends StackPane {

        private MediaPlayer mediaPlayer;
        private final CFImage cfImage;
        //
        private final HBox main;
        //
        private final Label titleLabel = getLabel("Title");
        private final Label artistLabel = getLabel("Artist");
        private final Button rewind = getButton(BoxiconsRegular.REWIND);
        private final Button play = getButton(BoxiconsRegular.PLAY);
        private final Button fastForward = getButton(BoxiconsRegular.FAST_FORWARD);
        private final FontIcon volume = FontIcon.of(BoxiconsSolid.VOLUME_FULL);
        private final MediaPlayerSlider volumeSlider;
        private final HBox volumeBox;
        private final HBox buttons;
        private final MediaPlayerSlider mediaPlayerSlider = new MediaPlayerSlider();
        private final Label timeLabel;
        private final ToggleButton animationButton;
        private AudioSpectrumBox audioSpectrumBox;
        //
        private final Rotate rotate = new Rotate();
        private Timeline rotateTimeline;

        private Label getLabel(String text) {
            Label label = new Label(text);
            label.getStyleClass().add("player-label");
            return label;
        }

        private Button getButton(Ikon ikon) {
            FontIcon fontIcon = FontIcon.of(ikon);
            Button button = new Button("", fontIcon);
            button.setMinSize(USE_PREF_SIZE, USE_PREF_SIZE);
            return button;
        }

        public AudioPlayerPane() {
            // 布局
            volumeSlider = new MediaPlayerSlider(0, 1, 0.5);
            volumeSlider.setMode(MediaPlayerSlider.Mode.ANY);
            volume.getStyleClass().add("volume");
            volume.iconCodeProperty().bind(Bindings.createObjectBinding((Callable<Ikon>) ()
                            -> volumeSlider.getValue() == 0 ? BoxiconsSolid.VOLUME_MUTE : BoxiconsSolid.VOLUME_FULL,
                    volumeSlider.valueProperty()));
            volumeBox = new HBox(volume, volumeSlider);
            volumeSlider.prefWidthProperty().bind(volumeBox.widthProperty());
            volumeBox.getStyleClass().add("volume-box");
            buttons = new HBox(rewind, play, fastForward, volumeBox);
            HBox.setHgrow(volumeBox, Priority.ALWAYS);
            buttons.getStyleClass().add("buttons");
            Line line = new Line();
            line.setStartX(0);
            line.getStrokeDashArray().addAll(5D, 5D);
            line.getStyleClass().add("line");
            VBox left = new VBox(titleLabel, artistLabel, line, buttons, mediaPlayerSlider);
            left.getStyleClass().add("left");
            cfImage = new CFImage();
            cfImage.setFit(CFImage.Fit.COVER);
            cfImage.arcProperty().bind(cfImage.heightProperty());
            cfImage.getTransforms().add(rotate);
            cfImage.setMinSize(USE_PREF_SIZE, USE_PREF_SIZE);
            // 图片裁剪
            cfImage.layoutBoundsProperty().addListener((observable, oldValue, newValue) -> {
                Rectangle rec = new Rectangle(newValue.getWidth(), newValue.getHeight());
                Circle circle = new Circle(newValue.getCenterX(), newValue.getCenterY(), 10);
                Shape subtract = Shape.subtract(rec, circle);
                cfImage.setClip(subtract);
            });
            main = new HBox(left, cfImage);
            left.prefWidthProperty().bind(widthProperty().subtract(cfImage.widthProperty()).subtract(main.spacingProperty()));
            line.endXProperty().bind(left.widthProperty().subtract(1));
            main.getStyleClass().add("main");
            //
            timeLabel = new Label("00:00 / 00:00");
            timeLabel.getStyleClass().add("time-label");
            StackPane.setAlignment(timeLabel, Pos.BOTTOM_LEFT);
            //
            animationButton = new ToggleButton("", FontIcon.of(BoxiconsSolid.BADGE));
            animationButton.getStyleClass().add("animation-button");
            StackPane.setAlignment(animationButton, Pos.TOP_RIGHT);
            StackPane.setMargin(animationButton, new Insets(10));
            getChildren().addAll(main, timeLabel, animationButton);
            animationButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
                if (newValue) {
                    if (audioSpectrumBox == null) {
                        audioSpectrumBox = new AudioSpectrumBox(cfImage.getWidth());
                    }
                    main.getChildren().set(1, audioSpectrumBox);
                } else {
                    main.getChildren().set(1, cfImage);
                }
            });
            //
            createMediaPlayer(getMedia());
            getSkinnable().mediaProperty().addListener((observable, oldValue, newValue) -> createMediaPlayer(newValue));
            rewind.setOnMouseClicked(event -> mediaPlayer.seek(mediaPlayer.getCurrentTime().subtract(Duration.seconds(5))));
            play.setOnMouseClicked(event -> {
                switch (mediaPlayer.getStatus()) {
                    case READY, PAUSED, STOPPED -> mediaPlayer.play();
                    case PLAYING -> mediaPlayer.pause();
                }
            });
            fastForward.setOnMouseClicked(event -> mediaPlayer.seek(mediaPlayer.getCurrentTime().add(Duration.seconds(5))));
            // 滑块值监听
            mediaPlayerSlider.valueProperty().addListener((observable, v1, v2) -> {
                boolean b = Math.abs(mediaPlayer.getCurrentTime().toMillis() - v2.doubleValue()) > 1000; // value值大于1秒
                if (b) {
                    Duration millis = Duration.millis(v2.doubleValue());
                    mediaPlayer.seek(millis);
                }
            });
        }

        private void createMediaPlayer(Media media) {
            if (media == null) {
                return;
            }
            if (mediaPlayer != null) {
                mediaPlayer.dispose();
            }
            mediaPlayer = new MediaPlayer(media);
            mediaPlayer.setAudioSpectrumNumBands(20); // 音频频谱中频带的数目
            mediaPlayer.volumeProperty().bindBidirectional(volumeSlider.valueProperty());
            mediaPlayer.setOnReady(() -> {
                ObservableMap<String, Object> metadata = media.getMetadata();
                Image image = (Image) metadata.get("image");
                String artist = (String) metadata.get("artist"); // 演唱者
                String album = (String) metadata.get("album"); // 专辑
                String title = (String) metadata.get("title"); // 标题
                artistLabel.setText(artist);
                titleLabel.setText(title);
                cfImage.setImage(image);
                mediaPlayerSlider.setNumber(0, media.getDuration().toMillis(), 0);
                timeLabel.setText(TimeUtils.getTimeStr(Duration.ZERO, media.getDuration()));
            });
            // 音频频谱监听
            mediaPlayer.setAudioSpectrumListener((timestamp, duration, magnitudes, phases) -> {
                if (animationButton.isSelected()) {
                    audioSpectrumBox.setAudioSpectrum(magnitudes);
                }
            });
            // 时间监听
            mediaPlayer.currentTimeProperty().addListener((observable, oldValue, newValue) -> {
                if (!mediaPlayerSlider.isPressed()) {
                    mediaPlayerSlider.setValue(newValue.toMillis());
                }
                timeLabel.setText(TimeUtils.getTimeStr(newValue, media.getDuration()));
            });
            // 状态监听
            mediaPlayer.statusProperty().addListener((observable, oldValue, newValue) -> {
                if (rotateTimeline == null) {
                    rotate.setPivotX(cfImage.getWidth() / 2);
                    rotate.setPivotY(cfImage.getHeight() / 2);
                    rotateTimeline = TimelineBuilder.builder()
                            .addKeyFrame(rotate.angleProperty(), 0, Duration.ZERO)
                            .addKeyFrame(rotate.angleProperty(), 360, Duration.seconds(10))
                            .cycleCount(Timeline.INDEFINITE)
                            .build();
                    rotateTimeline.statusProperty().addListener((observable1, v1, v2) ->
                            play.setGraphic(FontIcon.of(Animation.Status.RUNNING.equals(v2) ? BoxiconsRegular.PAUSE : BoxiconsRegular.PLAY)));
                }
                if (MediaPlayer.Status.PLAYING.equals(newValue)) {
                    rotateTimeline.play();
                } else {
                    rotateTimeline.pause();
                }
            });
            // 播放完毕
            mediaPlayer.setOnEndOfMedia(() -> mediaPlayer.stop());
        }

        public String getDurationStr(Duration duration) {
            int minutes = (int) duration.toMinutes();
            Duration subtract = duration.subtract(Duration.minutes(minutes));
            int seconds = (int) subtract.toSeconds();
            return String.format("%02d", minutes) + ":" + String.format("%02d", seconds);
        }

        public class AudioSpectrumBox extends HBox {

            public AudioSpectrumBox(double number) {
                setMaxSize(number, number);
                setMinSize(number, number);
                setAlignment(Pos.CENTER);
                setSpacing(2);
                ObservableList<Node> children = getChildren();
                for (int i = 0; i < mediaPlayer.getAudioSpectrumNumBands(); i++) {
                    children.add(getRectangle());
                }
            }

            public void setAudioSpectrum(float[] magnitudes) {
                int i = 0;
                for (float magnitude : magnitudes) {
                    Rectangle rectangle = (Rectangle) getChildren().get(i);
                    double s = 1 - (0 - magnitude) / 60;
                    rectangle.setHeight(140 * s < 1 ? 1 : 100 * s);
                    i++;
                }
            }

            private Rectangle getRectangle() {
                Rectangle rectangle = new Rectangle();
                rectangle.setWidth(4);
                rectangle.setHeight(1);
                rectangle.setArcWidth(4);
                rectangle.setArcHeight(4);
                rectangle.setFill(Color.rgb(255, 255, 255, 0.8));
                return rectangle;
            }
        }

    }

}
